What do the 'behavior' attribute on KeyboardAvoidingView ?
This article explains in depth the bahavior of the KeyboardAvoidingView attribute: behavior
What is the 'behavior' attribute on KeyboardAvoidingView ?
Introduction
React Native is a framework that have been in the landscape for many years (since 2015) and is widely used today. This fame brings several challenges, one of them is the Developer Experience (DX for short). DX partly relies on the documentation: a bad one can prevent the framework adoption. So, one job of the maintainers is to update/improve it with the help or not of the community.
About React Native, one of the documentation flows I noticed regards The 'KeyboardAvoidingView' component and the 'behavior' attribute. The documentation is quite terse on the subject unfortunately. That's the purpose of this article: trying to clarify the behavior of the attribute. What does it do ? For that we'll look at the source code.
Disclaimer: you can find the source code here
Overview
The behavior props is an enum of 3 values:
- height
- position
- padding
And as it is a facultative prop, a default behavior is implemented.
Each one adapts the position of the children components following the keyboard dimensions using a specific method. Using the 'height' css atribute or the 'padding' one for instance. The 'position' value acts differently and nor accordingly from its name, ie, doesn't use the 'position' css attribute.
Let's show some code ! All the logic is contained in the 'render' method and a switch case.
height
Here is an extract of the source code:
case 'height':
let heightStyle;
if (this._frame != null && this.state.bottom > 0) {
heightStyle = {
height: this._initialFrameHeight - bottomHeight,
flex: 0,
};
}
return (
<View
ref={this.viewRef}
style={StyleSheet.compose(
style,
heightStyle,
)}
onLayout={this._onLayout}
{...props}>
{children}
</View>
);
In this case, we check if the keyboard is present via the bottom
state variable (a state variable containing the height of the keyboard). And if yes, we create a view whose style
has a defined height
attribute equals to the substraction of
the frame heigth and the size of the keyboard. Also, a flex: 0
is set, meaning that the View
just takes the place defined by its heigth, no extra calculus done to make it bigger or smaller.
The condition is present to make the heigth revert if the user closes the keyboard, otherwise it would have been stucked.
So, this behavior is equivalent to create a View
component with a fixed defined height.
padding
Here is an extract of the source code:
case 'padding':
return (
<View
ref={this.viewRef}
style={StyleSheet.compose(
style,
{paddingBottom: bottomHeight},
)}
onLayout={this._onLayout}
{...props}>
{children}
</View>
);
For this behavior, the framework creates a View
component, as the previous one but this time we play with the paddingBottom
attribute, ie the component enlarges itself from the bottom
by a value defined by a bottomHeigth
, which is the height of the keyboard. So, we push the content of the view above to make place for the keyboard by making the container larger.
So, this behavior is equivalent to create a View
component with a bottom padding large enough to welcome the keyboard.
position
Here is an extract of the source code:
case 'position':
return (
<View
ref={this.viewRef}
style={style}
onLayout={this._onLayout}
{...props}>
<View
style={StyleSheet.compose(
contentContainerStyle,
{
bottom: bottomHeight,
},
)}>
{children}
</View>
</View>
);
This one differs from the previous. Indeed, in this case the View
is not enlarged but placed higher in the screen regardless of the other components. Its position is absolute.
Moreover, we create 2 nested views, one with the props and style and the other with the newly applied property plus another style provided by the contentContainerStyle
property.
So, this behavior is equivalent to create two Views
component with an absolute placement of the size of the keyboard.
default
Here is an extract of the source code:
default:
return (
<View
ref={this.viewRef}
onLayout={this._onLayout}
style={style}
{...props}>
{children}
</View>
);
As you can see, no extra styling are added so we fallback into the default configuration. It behaves differently according the OS:
- in android, it tries to adjust the size of the screen to let place to the keyboard thanks to the
adjustResize
attribute defined by default in the manifest - in ios, the view doesn't resizes so the keyboard overlaps the input. Indeed, by default, no attributes exists and some code is needed to do that
So this behavior lets the OS places the keyboard natively.
Conclusion
Unfortunately, we can't predict exactly the behavior according the OS and they both interact with this prop differently.
That's why we recommend:
- To set this prop and act as it is a mandatory one, by wrapping it with a component making it required. An example:
import {KeyboardAvoidingView as RNKeyboardAvoidingView} from 'react-native';
import {KeyboardAvoidingViewProps} from 'react-native/Libraries/Components/Keyboard/KeyboardAvoidingView';
interface KeyboardAvoidingViewType extends KeyboardAvoidingViewProps {}
const KeyboardAvoidingView = ({
behavior,
children,
...props
}: KeyboardAvoidingViewType) => {
return (
<RNKeyboardAvoidingView behavior={behavior} {...props}>
{children}
</RNKeyboardAvoidingView>
);
};
export default KeyboardAvoidingView;
2.To trial and error with the different possibilities, and not hesitating to change the value following the OS. For instance, in our test case (a simple text input emulated on a ios 14 and pixel xl), only the height attribute worked for and android and the other two only for ios.